﻿using System.Diagnostics;
using Tetris.Game.Common.TetrisCenter;
using Tetris.Game.Common.TetrisCenter.TetrisBase;
using Tetris.Game.ControlsBase;
using Tetris.Game.ControlsBase.Common;

namespace Tetris.Game.Common.Algorithm
{
    /// <summary>
    /// Pierre Dellacherie算法评估函数
    ///value = -landingHeight + erodedPieceCellsMetric - boardRowTransitions - boardColTransitions - (4 * boardBuriedHoles) - boardWells
    ///根据各指标的权重的经验值修改评估函数为：value = -45 × landingHeight + 34 × erodedPieceCellsMetric - 32 × boardRowTransitions - 93 × boardColTransitions - (79 × boardBuriedHoles) - 34 × boardWells
    ///其中：
    ///boardWells:各列中“井”的深度的连加和 
    ///boardColTransitions:这是每一列的变换次数之和
    /// boardBuriedHoles:各列中的“空洞的小方格数之和”
    /// boardRowTransitions:这是每一行的变换次数之和
    /// landingHeight:指当前板块放置之后，板块重心距离游戏区域底部的距离。（也就是小方块的海拔高度）
    /// erodedPieceCellsMetric:这是消除参数的体现，他代表的是消除的行数与当前摆放的板块中被消除的小方块的格数的乘积
    /// </summary>
    public unsafe class PierreDellacherie : IAlgorithm
    {
        private readonly int _rows = 0;
        private readonly int _cols = 0;

        public Func<ShapeType, bool, BlockShapeBase?>? CreationBlockShapeCallback { get; set; }

        public PierreDellacherie(int width = 10, int height = 20)
        {
            _rows = height;
            _cols = width;
        }
        public ChooseBestMoveResult ChooseBestMove(BlockShapeBase piece, BlockControl[,] grid)
        {
            var tempGrid = grid.DeepCloneBlockControlArray();
            int bestScore = int.MinValue;
            int bestX = 0;
            int bestY = 0;
            int bestRotation = 0;
            BlockShapeBase? testPiece = CreationBlockShapeCallback(piece.TetrisShapeType, false);
            testPiece.Move(tempGrid, DirectionType.Down);
            for (int rotation = 0; rotation < 4; rotation++)
            {
                int dx = 0;
                while (testPiece.Move(tempGrid, DirectionType.Right))
                {
                    ++dx;
                    int dy = tempGrid.DropHeight(testPiece);
                    int score = EvaluatePosition(testPiece, tempGrid,dy);
                    testPiece.Move(tempGrid, 0, -dy);// 重置位置
                    if (score > bestScore)
                    {
                        bestScore = score;
                        bestX = dx;
                        bestRotation = rotation;
                    }
                }
                testPiece.Move(tempGrid, -dx + 1, 0);// 重置位置
                dx = 0;
                while (testPiece.Move(tempGrid, DirectionType.Left))
                {
                    ++dx;
                    int dy = tempGrid.DropHeight(testPiece);
                    int score = EvaluatePosition(testPiece, tempGrid, dy);
                    testPiece.Move(tempGrid, 0, -dy);// 重置位置
                    if (score > bestScore)
                    {
                        bestScore = score;
                        bestX = -dx;
                        bestRotation = rotation;
                    }
                }

                testPiece.RotateShape(tempGrid);// 旋转
            }

            testPiece.Dispose();
            for (int i = 0; i < tempGrid.GetLength(0); i++)
            {
                for (int j = 0; j < tempGrid.GetLength(1); j++)
                {
                    BlockControl originalBlockControl = tempGrid[i, j];
                    if (originalBlockControl != null)
                    {
                        originalBlockControl.ExecBeginInvoke(() =>
                        {
                            originalBlockControl.Dispose();
                            originalBlockControl = null;
                        });
                    }
                }
            }
            return new ChooseBestMoveResult
            {
                BestScore = bestScore,
                BestX = bestX,
                Rotation = bestRotation
            };
        }
       
        /// <summary>
        /// 计算得分
        /// </summary>
        /// <param name="piece"></param>
        /// <param name="grid"></param>
        /// <returns></returns>
        private int EvaluatePosition(BlockShapeBase piece, BlockControl[,] grid,int landingHeight)
        {
            MergePieceToGrid(piece, grid);

            int score = 0;
            //landingHeight = -45 * landingHeight;
            //var erodedPieceCellsMetric = 34 * grid.ErodedPieceCellsMetric(piece);
            //var boardRowTransitions = -32 * grid.GetBoardRowTransitions();
            //var boardColTransitions = -93 * grid.GetBoardColTransitions();
            //var boardBuriedHoles = -79 * grid.GetBoardBuriedHoles();
            //var boardWells = -34 * grid.GetBoardWells();

            landingHeight = -1 * landingHeight;
            var erodedPieceCellsMetric = 1 * grid.ErodedPieceCellsMetric(piece);
            var boardRowTransitions = -1 * grid.GetBoardRowTransitions();
            var boardColTransitions = -1 * grid.GetBoardColTransitions();
            var boardBuriedHoles = -4 * grid.GetBoardBuriedHoles();
            var boardWells = -1 * grid.GetBoardWells();
            score = landingHeight + erodedPieceCellsMetric + boardRowTransitions + boardColTransitions + boardBuriedHoles + boardWells;
            return score;
        }
        /// <summary>
        /// 将饼图合并到网格
        /// </summary>
        /// <param name="piece"></param>
        /// <param name="grid"></param>
        private void MergePieceToGrid(BlockShapeBase piece, BlockControl[,] grid)
        {
            fixed (BlockControl* block = piece.BlockControls, set = grid)
            {
                for (int i = 0; i < piece.BlockControls.Length; i++)
                {
                    BlockControl* cur = block + i;
                    *(set + cur->PositionY * _cols + cur->PositionX) = *cur;
                }
            }
        }
       

        /// <summary>
        /// 指当前板块放置之后，板块重心距离游戏区域底部的距离（也就是小方块的海拔高度）,相当于高度越低越安全
        /// </summary>
        /// <param name="blockShapeBase"></param>
        /// <returns></returns>
        private int GetLandingHeight(BlockShapeBase blockShapeBase)
        {
            return _rows - blockShapeBase.Center.Y;
        }
        /// <summary>
        /// 这是消除参数的体现，他代表的是消除的行数与当前摆放的板块中被消除的小方块的格数的乘积。 
        /// 举个例子：下面这个例子就是说明红色的小方块下落之后会消除2行，而且自身贡献的小方格数是3个，所以返回值是3*2=6 
        /// </summary>
        /// <param name="blockShapeBase"></param>
        /// <returns></returns>
        private int GetErodedPieceCellsMetric(BlockShapeBase blockShapeBase, BlockControl[,] grid)
        {
            int i, j;
            int erodedPieceCellsMetric = 0;
            int minHeight = 0, maxHeight = 0, rowContribution = 0, fullRow = 0;
            bool isFull = false;
            foreach (var item in blockShapeBase.BlockControls)
            {
                if (minHeight > item.PositionY)
                    minHeight = item.PositionY;
                if (maxHeight < item.PositionY)
                    maxHeight = item.PositionY;
            }
            for (i = minHeight; i <= maxHeight; i++)
            {
                rowContribution = 0;
                isFull = false;
                for (j = 0; j < _cols; j++)
                {
                    BlockControl block = grid[i, j];
                    if (block == null)
                    {
                        isFull = false;
                        break;
                    }
                    // 检查块是否为下落形状的一部分
                    if (blockShapeBase.BlockControls.Contains(block))
                    {
                        rowContribution++;
                        isFull = true;
                    }
                }
                if (isFull)
                {
                    fullRow++;
                    erodedPieceCellsMetric += rowContribution;
                }
            }
            return erodedPieceCellsMetric * fullRow;
        }
    }
}
